#include "test_domain.h"
#include "test_types.h"

#include "nosal/core/result.h"
#include "nosal/core/error_code.h"

//#include "apd/test/gmock_matchers.h"

#include "gtest/gtest.h"
//#include "gmock/gmock.h"

#include <type_traits>

using namespace netaos;

using nosal::core::Result;
using nosal::core::ErrorCode;

//using apd::test::ArNoValue;
//using apd::test::ArHasValue;

//using ::testing::Eq;
//using ::testing::Ne;
//using ::testing::Not;

TEST(ResultTest, StaticTypeProperties)
{
    static_assert(std::is_same<Result<int>::value_type, int>::value, "wrong value_type definition");
    static_assert(std::is_same<Result<int>::error_type, ErrorCode>::value, "wrong error_type definition");

    static_assert(std::is_same<Result<int, long>::value_type, int>::value, "wrong value_type definition");
    static_assert(std::is_same<Result<int, long>::error_type, long>::value, "wrong error_type definition");

    SUCCEED();
}
#if 0
TEST(ResultTest, ConvertingConstructors)
{
    static_assert(std::is_constructible<Result<int>, int>::value, "Result<T> constructible from T");
    static_assert(std::is_constructible<Result<int>, ErrorCode>::value, "Result<T> constructible from E");
    static_assert(std::is_constructible<Result<int, long>, long>::value, "Result<T,E> constructible from T");
    static_assert(
        !std::is_constructible<Result<int, long>, ErrorCode>::value, "Result<T,E> not constructible from ErrorCode");

    static_assert(std::is_convertible<int, Result<int>>::value, "Result<T> implicitly convertible from T");
    static_assert(!std::is_convertible<ErrorCode, Result<int>>::value, "Result<T> not implicitly convertible from E");

    // Result(T const& t)
    int const value = 42;
    Result<int> r1(value);
    EXPECT_THAT(r1, ArHasValue(42));
    Result<int> r1a = value;
    EXPECT_THAT(r1a, ArHasValue(42));

    // Result(T&& t)
    Result<int> r2(42);
    EXPECT_THAT(r2, ArHasValue(42));
    Result<int> r2a = 42;
    EXPECT_THAT(r2a, ArHasValue(42));

    // explicit Result(E const& e)
    ErrorCode const ec(TestErrc::some_error);
    Result<int> r3(ec);
    EXPECT_THAT(r3, ArNoValue());
    // Result<int> r3a = ec;  // OK: does not compile!

    // explicit Result(E&& e)
    Result<int> r4(ErrorCode{TestErrc::some_error});
    EXPECT_THAT(r4, ArNoValue());
    // Result<int> r4a = ErrorCode(TestErrc::some_error);  // OK: does not compile!
}

TEST(ResultTest, CopyInt)
{
    Result<int> const source(42);
    Result<int> copy_result(source);
    EXPECT_THAT(copy_result, ArHasValue(42));

    Result<int> const other_source(44);
    copy_result = other_source;
    EXPECT_THAT(copy_result, ArHasValue(44));
}

TEST(ResultTest, MoveInt)
{
    Result<int> source(42);
    Result<int> move_result(std::move(source));
    EXPECT_THAT(move_result, ArHasValue(42));
    EXPECT_THAT(source, ArHasValue(42));  // source not changed after move

    move_result = Result<int>(45);
    EXPECT_THAT(move_result, ArHasValue(45));
}

TEST(ResultTest, CopyString)
{
    Result<std::string> const source("42");
    Result<std::string> copy_result(source);
    EXPECT_THAT(copy_result, ArHasValue("42"));

    Result<std::string> const other_source("44");
    copy_result = other_source;
    EXPECT_THAT(copy_result, ArHasValue("44"));
}

TEST(ResultTest, MoveString)
{
    Result<std::string> source("42");
    Result<std::string> move_result(std::move(source));
    EXPECT_THAT(move_result, ArHasValue("42"));
    EXPECT_THAT(source, ArHasValue(""));  // source zeroed after move

    move_result = Result<std::string>("45");
    EXPECT_THAT(move_result, ArHasValue("45"));
}
TEST(ResultTest, HasValue)
{
    static_assert(std::is_constructible<bool, Result<int>>::value, "Result<T> convertible to bool");
    static_assert(!std::is_convertible<Result<int>, bool>::value, "Result<T> not implicitly convertible to bool");

    Result<int> const r1(42);
    Result<int> const r2(ErrorCode{TestErrc::some_error});

    // bool HasValue() const noexcept
    EXPECT_TRUE(r1.HasValue());
    EXPECT_FALSE(r2.HasValue());

    // explicit operator bool() const noexcept
    EXPECT_TRUE(bool(r1));
    EXPECT_FALSE(bool(r2));
}

TEST(ResultTest, Swap)
{
    Result<int> r1(42);
    Result<int> r2(43);

    r1.Swap(r2);
    EXPECT_THAT(r1, ArHasValue(43));
    EXPECT_THAT(r2, ArHasValue(42));

    Result<int> r3(ErrorCode{TestErrc::some_error});
    Result<int> r4(ErrorCode{TestErrc::another_error});
    r3.Swap(r4);
    EXPECT_THAT(r3, ArNoValue());
    EXPECT_THAT(r4, ArNoValue());
    EXPECT_THAT(r3.Error(), ErrorCode{TestErrc::another_error});
    EXPECT_THAT(r4.Error(), ErrorCode{TestErrc::some_error});

    r3.Swap(r1);
    EXPECT_THAT(r3, ArHasValue(43));
    EXPECT_THAT(r1, ArNoValue());
    EXPECT_THAT(r1.Error(), ErrorCode{TestErrc::another_error});

    r3.Swap(r1);
    EXPECT_THAT(r1, ArHasValue(43));
    EXPECT_THAT(r3, ArNoValue());
    EXPECT_THAT(r3.Error(), ErrorCode{TestErrc::another_error});
}

TEST(ResultTest, DereferenceOperator)
{
    Result<int> const r1(42);

    // T const& operator*() const&
    EXPECT_THAT(*r1, Eq(42));

    // T&& operator*() &&
    auto f = []() -> Result<std::string> { return std::string("foobar"); };
    EXPECT_THAT(*(f()), Eq("foobar"));
}

TEST(ResultTest, Dereference_Void)
{
    Result<void> const r1;
    *r1;
    SUCCEED();
}

TEST(ResultTest, Emplace)
{
    NonLiteral const v1{43};
    ErrorCode const ec1 = TestErrc::some_error;

    Result<NonLiteral> r1{NonLiteral{42}};
    r1.EmplaceValue(43);
    EXPECT_THAT(r1, ArHasValue(v1));

    r1.EmplaceError(TestErrc::some_error);
    EXPECT_THAT(r1, ArNoValue());
    EXPECT_THAT(r1.Error(), ec1);
}

#endif

TEST(ResultTest, Emplace_Void)
{
    ErrorCode const ec1 = TestErrc::some_error;

    Result<void> r1;
    r1.EmplaceValue();
    *r1;
    EXPECT_TRUE(r1.HasValue());

    r1.EmplaceError(TestErrc::some_error);
    EXPECT_FALSE(r1.HasValue());
    EXPECT_EQ(r1.Error(), ec1);
}

TEST(ResultTest, Ok)
{
    // Optional<T> Ok() const&

    Result<int> const r1(42);
    nosal::core::Optional<int> const v1 = r1.Ok();
    EXPECT_EQ(v1.has_value(), true);
    EXPECT_EQ(*v1, 42);

    Result<int> const r2(ErrorCode{TestErrc::some_error});
    nosal::core::Optional<int> const v2 = r2.Ok();
    EXPECT_EQ(v2.has_value(), false);

    // Optional<T> Ok() &&

    nosal::core::Optional<int> const v3 = Result<int>(44).Ok();
    EXPECT_EQ(v3.has_value(), true);
    EXPECT_EQ(*v3, 44);

    nosal::core::Optional<int> const v4 = Result<int>(ErrorCode{TestErrc::some_error}).Ok();
    EXPECT_EQ(v4.has_value(), false);
}

TEST(ResultTest, Err)
{
    // Optional<E> Err() const&

    Result<int> const r1(42);
    nosal::core::Optional<ErrorCode> const v1 = r1.Err();
    EXPECT_EQ(v1.has_value(), false);

    Result<int> const r2(ErrorCode{TestErrc::some_error});
    nosal::core::Optional<ErrorCode> const v2 = r2.Err();
    EXPECT_EQ(v2.has_value(), true);
    EXPECT_EQ(*v2, TestErrc::some_error);

    // Optional<E> Err() &&

    nosal::core::Optional<ErrorCode> const v3 = Result<int>(44).Err();
    EXPECT_EQ(v3.has_value(), false);

    nosal::core::Optional<ErrorCode> const v4 = Result<int>(ErrorCode{TestErrc::some_error}).Err();
    EXPECT_EQ(v4.has_value(), true);
    EXPECT_EQ(*v4, TestErrc::some_error);
}

TEST(ResultTest, Err_Void)
{
    // Optional<E> Err() const&

    Result<void> const r1;
    nosal::core::Optional<ErrorCode> const e1 = r1.Err();
    EXPECT_EQ(e1.has_value(), false);

    Result<void> const r2(ErrorCode{TestErrc::some_error});
    nosal::core::Optional<ErrorCode> const v2 = r2.Err();
    EXPECT_EQ(v2.has_value(), true);
    EXPECT_EQ(*v2, TestErrc::some_error);

    // Optional<E> Err() &&

    nosal::core::Optional<ErrorCode> const v3 = Result<void>().Err();
    EXPECT_EQ(v3.has_value(), false);

    nosal::core::Optional<ErrorCode> const v4 = Result<void>(ErrorCode{TestErrc::some_error}).Err();
    EXPECT_EQ(v4.has_value(), true);
    EXPECT_EQ(*v4, TestErrc::some_error);
}

// An enum type used as T for Result tests below
enum class ValueEnum
{
    first_value = 10,
    second_value = 11,
};

TEST(ResultTest, ValueOr)
{
    // template <typename U>
    // T ValueOr(U&& defaultValue) const&

    Result<int> const r1(42);
    int const v1 = r1.ValueOr(17);
    EXPECT_EQ(v1, 42);

    Result<int> const r2(ErrorCode{TestErrc::some_error});
    int const v2 = r2.ValueOr(17);
    EXPECT_EQ(v2, 17);

    // The default value is static_cast'd to the Result's value type, and this is a test
    // where that makes a difference: Using a plain int as default value for a Result
    // that holds an enum.
    Result<ValueEnum> const r3(ErrorCode{TestErrc::some_error});
    ValueEnum v3 = r3.ValueOr(11);
    EXPECT_EQ(v3, ValueEnum::second_value);

    // template <typename U>
    // T ValueOr(U&& defaultValue) &&

    int const v11 = Result<int>(42).ValueOr(17);
    EXPECT_EQ(v11, 42);

    int const v12 = Result<int>(ErrorCode{TestErrc::some_error}).ValueOr(17);
    EXPECT_EQ(v12, 17);

    ValueEnum const v13 = Result<ValueEnum>(ErrorCode{TestErrc::some_error}).ValueOr(11);
    EXPECT_EQ(v13, ValueEnum::second_value);
}

TEST(ResultTest, ValueOr_Void)
{
    Result<void> const r1;
    r1.ValueOr(42);
    SUCCEED();
}

TEST(ResultTest, Resolve)
{
    Result<int> const r1(42);
    int const v1 = r1.Resolve([](ErrorCode const&) { return 44; });
    EXPECT_EQ(v1, 42);

    Result<int> const r2(ErrorCode{TestErrc::some_error});
    int const v2 = r2.Resolve([](ErrorCode const&) { return 44; });
    EXPECT_EQ(v2, 44);
}

TEST(ResultTest, Resolve_Void)
{
    Result<void> const r1;
    r1.Resolve([](ErrorCode const&) { return; });
    SUCCEED();
}

#ifndef NOSAL_NO_EXCEPTIONS
TEST(ResultTest, ValueOrThrow_Void)
{
    Result<void> const r1;
    r1.ValueOrThrow();
    SUCCEED();

    Result<void> const r2(ErrorCode{TestErrc::some_error});
    EXPECT_THROW(r2.ValueOrThrow(), TestException);
}
#endif  // NOSAL_NO_EXCEPTIONS

TEST(ResultTest, ErrorOr)
{
    // template <typename G>
    // E ErrorOr(G&& defaultError) const&

    Result<int> r1 = 42;
    EXPECT_EQ(r1.ErrorOr(ErrorCode(TestErrc::some_error)), ErrorCode(TestErrc::some_error));

    Result<int> r2(ErrorCode{TestErrc::some_error});
    EXPECT_EQ(r2.ErrorOr(ErrorCode(TestErrc::another_error)), ErrorCode(TestErrc::some_error));

    // template <typename G>
    // E ErrorOr(G&& defaultError) &&

    EXPECT_EQ(Result<int>(42).ErrorOr(ErrorCode(TestErrc::some_error)), ErrorCode(TestErrc::some_error));

    EXPECT_EQ(Result<int>(ErrorCode(TestErrc::some_error)).ErrorOr(ErrorCode(TestErrc::another_error)),
        ErrorCode(TestErrc::some_error));
}

TEST(ResultTest, ErrorOr_Void)
{
    // template <typename G>
    // E ErrorOr(G&& defaultError) const&

    Result<void> r1;
    EXPECT_EQ(r1.ErrorOr(ErrorCode(TestErrc::some_error)), ErrorCode(TestErrc::some_error));

    Result<void> r2(ErrorCode{TestErrc::some_error});
    EXPECT_EQ(r2.ErrorOr(ErrorCode(TestErrc::another_error)), ErrorCode(TestErrc::some_error));

    // template <typename G>
    // E ErrorOr(G&& defaultError) &&

    EXPECT_EQ(Result<void>{}.ErrorOr(ErrorCode(TestErrc::some_error)), ErrorCode(TestErrc::some_error));

    EXPECT_EQ(Result<void>(ErrorCode(TestErrc::some_error)).ErrorOr(ErrorCode(TestErrc::another_error)),
        ErrorCode(TestErrc::some_error));
}

TEST(ResultTest, CheckError)
{
    // template <typename G>
    // bool CheckError(G&& e) const

    Result<int> const r1(1);
    Result<int> const r2(ErrorCode{TestErrc::some_error});
    Result<int> const r3(ErrorCode{TestErrc::another_error});

    bool v1a = r1.CheckError(TestErrc::some_error);
    bool v1b = r1.CheckError(TestErrc::another_error);
    EXPECT_FALSE(v1a);
    EXPECT_FALSE(v1b);

    bool v2a = r2.CheckError(TestErrc::some_error);
    bool v2b = r2.CheckError(TestErrc::another_error);
    EXPECT_TRUE(v2a);
    EXPECT_FALSE(v2b);

    bool v3a = r3.CheckError(TestErrc::some_error);
    bool v3b = r3.CheckError(TestErrc::another_error);
    EXPECT_FALSE(v3a);
    EXPECT_TRUE(v3b);

    // The argument is static_cast'd to the Result's error type, and this is a test
    // where that makes a difference: Using a plain int as check argument for a Result
    // that holds an enum as 'E' type.
    Result<int, ValueEnum> const r11(ValueEnum::first_value);
    bool v11a = r11.CheckError(10);
    bool v11b = r11.CheckError(11);
    EXPECT_TRUE(v11a);
    EXPECT_FALSE(v11b);
}

#if 0
struct DataType
{
    int mValue;

    explicit DataType(int v)
        : mValue(v)
    { }

    int Value() const
    {
        return mValue;
    }
};

TEST(ResultTest, Bind)
{
    auto f1 = [](DataType const& data) -> Result<int> { return Result<int>::FromValue(data.Value()); };
    auto f2 = [](DataType const& data) -> int { return data.Value(); };

    // template <typename F, typename = CallableReturnsResult<F>>
    // auto Bind(F&& f) const -> decltype(f(Value()))

    Result<DataType> const r1(DataType(1));
    Result<int> const r1a = r1.Bind(f1);
    EXPECT_THAT(r1a, ArHasValue(1));

    Result<DataType> const r3(ErrorCode{TestErrc::some_error});
    Result<int> const r3a = r3.Bind(f1);
    EXPECT_THAT(r3a, ArNoValue());
    EXPECT_TRUE(r3a.CheckError(TestErrc::some_error));

    // template <typename F, typename = CallableReturnsNoResult<F>>
    // auto Bind(F&& f) const -> Result<decltype(f(Value())), E>

    Result<DataType> const r2(DataType(2));
    Result<int> const r2a = r2.Bind(f2);
    EXPECT_THAT(r2a, ArHasValue(2));

    Result<DataType> const r4(ErrorCode{TestErrc::some_error});
    Result<int> const r4a = r4.Bind(f2);
    EXPECT_THAT(r4a, ArNoValue());
    EXPECT_TRUE(r4a.CheckError(TestErrc::some_error));
}

TEST(ResultTest, Bind_Void)
{
    auto f1 = []() -> Result<int> { return Result<int>::FromValue(1); };
    auto f2 = []() -> int { return 2; };

    // template <typename F, typename = CallableReturnsResult<F>>
    // auto Bind(F&& f) const -> decltype(f())

    Result<void> const r1;
    Result<int> const r1a = r1.Bind(f1);
    EXPECT_THAT(r1a, ArHasValue(1));

    DataType const data1(11);
    Result<int> const r1b = r1.Bind([&data1]() { return data1.Value(); });
    EXPECT_THAT(r1b, ArHasValue(11));

    Result<void> const r3(ErrorCode{TestErrc::some_error});
    Result<int> const r3a = r3.Bind(f1);
    EXPECT_THAT(r3a, ArNoValue());
    EXPECT_TRUE(r3a.CheckError(TestErrc::some_error));

    // template <typename F, typename = CallableReturnsNoResult<F>>
    // auto Bind(F&& f) const -> Result<decltype(f()), E>

    Result<void> const r2;
    Result<int> const r2a = r2.Bind(f2);
    EXPECT_THAT(r2a, ArHasValue(2));

    DataType const data2(22);
    Result<int> const r2b = r2.Bind([&data2]() { return data2.Value(); });
    EXPECT_THAT(r2b, ArHasValue(22));

    Result<void> const r4(ErrorCode{TestErrc::some_error});
    Result<int> const r4a = r4.Bind(f2);
    EXPECT_THAT(r4a, ArNoValue());
    EXPECT_TRUE(r4a.CheckError(TestErrc::some_error));
}
#endif
TEST(ResultTest, Comparisons)
{
    int const v1 = 1;
    int const v2 = 2;
    ErrorCode const ec1 = TestErrc::some_error;

    Result<int> const r1(v1);
    Result<int> const r1a(1);
    Result<int> const r2(v2);
    Result<int> const r3(ErrorCode{TestErrc::some_error});
    Result<int> const r3a(ErrorCode{TestErrc::some_error});
    Result<int> const r4(ErrorCode{TestErrc::another_error});

    // template <typename T, typename E>
    // bool operator==(const Result<T, E>& lhs, const Result<T, E>& rhs)
    EXPECT_EQ(r1, r1a);
    EXPECT_NE(r1, r2);
    EXPECT_NE(r1, r3);

    EXPECT_EQ(r3, r3a);
    EXPECT_NE(r3, r4);
    EXPECT_NE(r3, r2);

    // template <typename T, typename E>
    // bool operator!=(const Result<T, E>& lhs, const Result<T, E>& rhs)
    //EXPECT_THAT(r1, Not(Ne(r1a)));
    //EXPECT_THAT(r1, Ne(r2));
    //EXPECT_THAT(r1, Ne(r3));

    //EXPECT_THAT(r3, Not(Ne(r3a)));
    //EXPECT_THAT(r3, Ne(r4));
    //EXPECT_THAT(r3, Ne(r2));

    // template <typename T, typename E>
    // bool operator==(const Result<T, E>& lhs, const T& rhs)

    EXPECT_EQ(r1, v1);
    EXPECT_EQ(r1, 1);
    EXPECT_NE(r1, v2);
    EXPECT_NE(r1, 2);

    // template <typename T, typename E>
    // bool operator==(const T& lhs, const Result<T, E>& rhs)

    EXPECT_EQ(v1, r1);
    EXPECT_EQ(1, r1);
    EXPECT_NE(v2, r1);
    EXPECT_NE(2, r1);

    // template <typename T, typename E>
    // bool operator!=(const Result<T, E>& lhs, const T& rhs);

    //EXPECT_THAT(r1, Not(Ne(v1)));
    //EXPECT_THAT(r1, Not(Ne(1)));
    //EXPECT_THAT(r1, Ne(v2));
    //EXPECT_THAT(r1, Ne(2));

    // template <typename T, typename E>
    // bool operator!=(const T& lhs, const Result<T, E>& rhs);

    //EXPECT_THAT(v1, Not(Ne(r1)));
    //EXPECT_THAT(1, Not(Ne(r1)));
    //EXPECT_THAT(v2, Ne(r1));
    //EXPECT_THAT(2, Ne(r1));

    // template <typename T, typename E>
    // bool operator==(const Result<T, E>& lhs, const E& rhs)

    EXPECT_NE(r1, ec1);
    EXPECT_EQ(r3, ec1);

    // template <typename T, typename E>
    // bool operator==(const E& lhs, const Result<T, E>& rhs);

    EXPECT_NE(ec1, r1);
    EXPECT_EQ(ec1, r3);

    // template <typename T, typename E>
    // bool operator!=(const Result<T, E>& lhs, const E& rhs);

    //EXPECT_THAT(r1, Ne(ec1));
    //EXPECT_THAT(r3, Not(Ne(ec1)));

    // template <typename T, typename E>
    // bool operator!=(const E& lhs, const Result<T, E>& rhs);

    //EXPECT_THAT(ec1, Ne(r1));
    //EXPECT_THAT(ec1, Not(Ne(r3)));
}
